package com.gourd.common.handler;

import com.gourd.common.data.ApiError;
import com.gourd.common.data.BaseResponse;
import com.gourd.common.exception.ServiceException;
import com.gourd.common.exception.EntityExistException;
import com.gourd.common.exception.EntityNotFoundException;
import com.gourd.common.utils.ThrowableUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

import static org.springframework.http.HttpStatus.*;

/**
 * 异常处理器
 * @author gourd
 */
@RestControllerAdvice
@Slf4j
public class BusinessExceptionHandler {

	/**
	 * 应用到所有@RequestMapping注解方法，在其执行之前初始化数据绑定器
	 * @param binder
	 */
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		System.out.println("请求有参数才进来 "+binder.getObjectName());
	}
 
	/**
	 * 把值绑定到Model中，使全局@RequestMapping可以获取到该值
	 * @param model
	 */
	@ModelAttribute
	public void addAttributes(Model model) {
		model.addAttribute("author", "gourd");
	}


	/**
	 * 处理 接口无权访问异常AccessDeniedException
	 * @param e
	 * @return
	 */
	@ExceptionHandler(AccessDeniedException.class)
	public ResponseEntity handleAccessDeniedException(AccessDeniedException e){
		// 打印堆栈信息
		log.error(ThrowableUtil.getStackTrace(e));
		ApiError apiError = new ApiError(FORBIDDEN.value(),e.getMessage());
		return buildResponseEntity(apiError);
	}

	/**
	 * 处理自定义异常
	 * @param e
	 * @return
	 */
	@ExceptionHandler(value = ServiceException.class)
	public ResponseEntity<ApiError> badRequestException(ServiceException e) {
		// 打印堆栈信息
		log.error(ThrowableUtil.getStackTrace(e));
		ApiError apiError = new ApiError(e.getStatus(),e.getMessage());
		return buildResponseEntity(apiError);
	}

	/**
	 * 处理 EntityExist
	 * @param e
	 * @return
	 */
	@ExceptionHandler(value = EntityExistException.class)
	public ResponseEntity<ApiError> entityExistException(EntityExistException e) {
		// 打印堆栈信息
		log.error(ThrowableUtil.getStackTrace(e));
		ApiError apiError = new ApiError(BAD_REQUEST.value(),e.getMessage());
		return buildResponseEntity(apiError);
	}

	/**
	 * 处理 EntityNotFound
	 * @param e
	 * @return
	 */
	@ExceptionHandler(value = EntityNotFoundException.class)
	public ResponseEntity<ApiError> entityNotFoundException(EntityNotFoundException e) {
		// 打印堆栈信息
		log.error(ThrowableUtil.getStackTrace(e));
		ApiError apiError = new ApiError(NOT_FOUND.value(),e.getMessage());
		return buildResponseEntity(apiError);
	}
	/**
	 * 处理所有不可知的异常
	 * @param e
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	public Object handleException(Exception e, HttpServletRequest req){
		BaseResponse response = new BaseResponse();
		response.setCode(INTERNAL_SERVER_ERROR.value());
		if(e.getMessage().length()>500){
			response.setMsg("未知异常，请联系管理员");
		}else {
			response.setMsg(e.getMessage());
		}
		//使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
		String contentTypeHeader = req.getHeader("Content-Type");
		String acceptHeader = req.getHeader("Accept");
		String xRequestedWith = req.getHeader("X-Requested-With");
		// 控制台打印log
		e.printStackTrace();
		if ((contentTypeHeader != null && contentTypeHeader.contains("application/json"))
				|| (acceptHeader != null && acceptHeader.contains("application/json"))
				|| "XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
			return response;
		} else {
			ModelAndView modelAndView = new ModelAndView();
			modelAndView.addObject("msg", e.getMessage());
			modelAndView.addObject("url", req.getRequestURL());
			modelAndView.addObject("stackTrace", e.getStackTrace());
			modelAndView.setViewName("error");
			return modelAndView;
		}
	}

	/**
	 * 统一返回
	 * @param apiError
	 * @return
	 */
	private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
		return new ResponseEntity(apiError, HttpStatus.valueOf(apiError.getStatus()));
	}
}